package com.apestech.api.workflow.lib.element;

import com.apestech.framework.json.JSONArray;
import com.apestech.framework.json.JSONObject;
import com.apestech.framework.util.StringFuncUtil;
import com.apestech.framework.util.StringUtil;
import org.flowable.bpmn.model.*;

import java.util.List;

/**
 * 工作流结点
 *
 * @author hup
 * @create 2018-07-03-10:53
 */
public class FlowNodeInfo extends StringFuncUtil{

    public enum FlowOper {
        NEXT(0), PRE(1), ALL(2);

        int value;

        FlowOper(int value) {
            this.value = value;
        }

        public int getValue() {
            return value;
        }
    }

    private FlowElement flow;
    private FlowElement targetFlow;
    private List<SequenceFlow> seqlist;
    private String conditionExpression;
    private String conValue;
    private String conId;
    private String exp;
    private String targetid;
    private String targetname;
    private JSONArray nodelist;
    private String id;
    private String name;
    private String seqId;
    private String seqName;
    private FlowElement sourceFlow;
    private FlowOper flowOper=FlowOper.NEXT;

    public FlowNodeInfo(FlowElement flow, FlowOper flowOper) throws Exception {
        this.flowOper=flowOper;
        iniFlowInfo(flow);
    }

    public FlowNodeInfo(FlowElement flow) throws Exception {
        this.flowOper=FlowOper.NEXT;
        iniFlowInfo(flow);
    }

    private void iniFlowInfo(FlowElement flow) throws Exception {
        this.flow=flow;
        this.id=flow.getId();
        this.name=flow.getName();
        if(flow instanceof StartEvent){
            iniStartEvent();
        } else if(flow instanceof UserTask){
            iniUserTask();
        } else if(flow instanceof ServiceTask){
            iniServiceTask();
        } else if(flow instanceof CallActivity){
            iniCallActivity();
        } else if(flow instanceof ExclusiveGateway){
            iniExclusiveGateway();
        } else if(flow instanceof EndEvent){
            iniEndEvent();
        }
    }

    private void iniStartEvent() throws Exception {
        StartEvent f= (StartEvent) this.flow;
        if(flowOper==FlowOper.NEXT || flowOper==FlowOper.ALL){
            seqlist=f.getOutgoingFlows();
            getNextNode();
        }
        if(flowOper==FlowOper.PRE || flowOper==FlowOper.ALL){
            getSourceFlow(f.getIncomingFlows());
        }
    }

    private void iniUserTask() throws Exception {
        UserTask f= (UserTask) this.flow;
        if(flowOper==FlowOper.NEXT || flowOper==FlowOper.ALL){
            seqlist=f.getOutgoingFlows();
            getNextNode();
        }
        if(flowOper==FlowOper.PRE || flowOper==FlowOper.ALL){
            getSourceFlow(f.getIncomingFlows());
        }
    }

    private void iniServiceTask() throws Exception {
        ServiceTask f= (ServiceTask) this.flow;
        if(flowOper==FlowOper.NEXT || flowOper==FlowOper.ALL){
            seqlist=f.getOutgoingFlows();
            getNextNode();
        }
        if(flowOper==FlowOper.PRE || flowOper==FlowOper.ALL){
            getSourceFlow(f.getIncomingFlows());
        }
    }

    private void iniCallActivity() throws Exception {
        CallActivity f= (CallActivity) this.flow;
        if(flowOper==FlowOper.NEXT || flowOper==FlowOper.ALL){
            seqlist=f.getOutgoingFlows();
            getNextNode();
        }
        if(flowOper==FlowOper.PRE || flowOper==FlowOper.ALL){
            getSourceFlow(f.getIncomingFlows());
        }
    }

    private void iniExclusiveGateway() throws Exception {
        ExclusiveGateway f= (ExclusiveGateway) this.flow;
        if(flowOper==FlowOper.NEXT || flowOper==FlowOper.ALL){
            seqlist=f.getOutgoingFlows();
            getNextNode();
        }
        if(flowOper==FlowOper.PRE || flowOper==FlowOper.ALL){
            getSourceFlow(f.getIncomingFlows());
        }
    }

    private void iniEndEvent(){
        EndEvent f= (EndEvent) this.flow;
        seqlist=f.getOutgoingFlows();
        if(flowOper==FlowOper.PRE || flowOper==FlowOper.ALL){
            getSourceFlow(f.getIncomingFlows());
        }
    }

    public void isGatewayConditionCorrect(JSONObject input) throws Exception {
        if(nodelist==null || nodelist.size()==0) return;
        int len=nodelist.size();
        boolean flag=false;
        for(int i=0; i<len; i++){
            JSONObject m= (JSONObject) nodelist.get(i);
            String condition=m.getString("condition");
            flag= (boolean) doExpression(condition, input);
            if(flag){
                break;
            }
        }
        if(!flag){
            throw new Exception("没有符合路由条件数据存在，请检查业务数据!");
        }
    }

    private void getNextNode() throws Exception {
        nodelist=new JSONArray();
        int len=seqlist.size();
        for(int i=0; i<len; i++){
            iniSequenceFlow(seqlist.get(i));
            if(targetFlow instanceof ExclusiveGateway){
                FlowNodeInfo node=new FlowNodeInfo(targetFlow);
                List<SequenceFlow> list=node.getSeqlist();
                int jlen=list.size();
                for(int j=0; j<jlen; j++){
                    node.iniSequenceFlow(list.get(j));
                    JSONObject m=new JSONObject();
                    m.put("targetId", node.getTargetid());   //目标任务ID
                    m.put("targetName", node.getTargetname());   //目标任务NAME
                    m.put("id", node.getSeqId());     //箭头ID
                    m.put("name", node.getSeqName());    //箭头名称
                    m.put("valueId", node.getConId());     //公式中ID
                    String value=node.getConValue();
                    //if(StringUtil.isNumber(value)){
                    //    m.put("value",castToInt(value));
                    //} else {
                        m.put("value", value);
                    //}
                    //m.put("value", node.getConValue().startsWith("'") ? node.getConValue() : Double.valueOf(node.getConValue()));   //公式中值
                    m.put("express", node.getExp());    //公式全部内容
                    m.put("condition", node.getConditionExpression());  //公式中的表达式(==, >, >=, <, <=)
                    nodelist.add(m);
                }
                this.setNodelist(nodelist);
            }
        }
    }

    public void iniSequenceFlow(SequenceFlow f) throws Exception {
        seqId=f.getId();
        seqName=f.getName();
        targetid=f.getTargetFlowElement().getId();
        targetname=f.getTargetFlowElement().getName();
        conditionExpression=f.getConditionExpression();
        if(conditionExpression!=null){
            conditionExpression=conditionExpression.substring(2,conditionExpression.length()-1);
            int pos=conditionExpression.indexOf("==");
            exp="==";
            if(pos<0){
                pos=conditionExpression.indexOf(">=");
                exp=">=";
                if(pos<0) {
                    pos = conditionExpression.indexOf("<=");
                    exp="<=";
                }
                if(pos<0) {
                    pos = conditionExpression.indexOf(">");
                    exp=">";
                }
                if(pos<0) {
                    pos = conditionExpression.indexOf("<");
                    exp="<";
                }
            }
            if(pos>=0){
                conValue=conditionExpression.substring(pos+exp.length(), conditionExpression.length());
                conValue=conValue.replace("'","").trim();
                conId=conditionExpression.substring(0, pos).trim();
            } else {
                exp=null;
            }
        }
        targetFlow=f.getTargetFlowElement();
    }

    private void getSourceFlow(List<SequenceFlow> inlist){
        sourceFlow=null;
        if(inlist==null){
            return;
        }
        if(inlist.size()>0){
            sourceFlow=inlist.get(0).getSourceFlowElement();
        }
    }

    public List<SequenceFlow> getSeqlist() {
        return seqlist;
    }

    public String getNodeType(){
        return this.flow.getClass().getName();
    }

    public String getConditionExpression() {
        return conditionExpression;
    }

    public String getConValue() {
        return conValue;
    }

    public String getExp() {
        return exp;
    }

    public String getTargetid() {
        return targetid;
    }

    public String getTargetname() {
        return targetname;
    }

    public JSONArray getNodelist() {
        if(nodelist==null || nodelist.size()==0) return null;
        return nodelist;
    }

    public void setNodelist(JSONArray nodelist) {
        this.nodelist = nodelist;
    }

    public String getId() {
        return id;
    }

    public String getName() {
        return name;
    }

    public String getSeqId() {
        return seqId;
    }

    public String getSeqName() {
        return seqName;
    }

    public String getConId() {
        return conId;
    }

    public FlowElement getSourceFlow() {
        return sourceFlow;
    }

    public boolean isSourceUserTask(){
        if(sourceFlow!=null){
            return sourceFlow instanceof UserTask;
        }
        return false;
    }

    public boolean isTargetUserTask(){
        if(targetFlow!=null){
            return targetFlow instanceof UserTask;
        }
        return false;
    }


}
